#ifndef __COROUTINE_7IN7OUT_H__
#define __COROUTINE_7IN7OUT_H__

#include <coroutine>
#include <iostream>

namespace coroutine_7in7out
{
	template <typename T>
	struct coro_ret
	{
		struct promise_type;
		using handle_type = std::coroutine_handle<promise_type>;

		coro_ret(handle_type handle)
			: m_coro_handle{ handle }
		{
			std::cout << __func__ << std::endl;
		}

		~coro_ret()
		{
			if (m_coro_handle)
			{
				m_coro_handle.destroy();

				std::cout << __func__ << std::endl;
			}
		}

		// 复制
		coro_ret(const coro_ret&) = delete;
		coro_ret& operator=(const coro_ret&) = delete;

		// 移动
		coro_ret(coro_ret&& other) noexcept
			: m_coro_handle{ other.m_coro_handle }
		{
			other.m_coro_handle = nullptr;
		}

		coro_ret& operator=(coro_ret&& other) noexcept
		{
			if (this != &other)
			{
				this->m_coro_handle = other.m_coro_handle;
				other.m_coro_handle = nullptr;
			}

			return *this;
		}

		//
		bool move_next()
		{
			m_coro_handle.resume();

			std::cout << __func__ << std::endl;

			return m_coro_handle.done();
		}

		T get()
		{
			return m_coro_handle.promise().m_return_t;
		}

		// promise_type
		struct promise_type
		{
			promise_type() = default;
			~promise_type() = default;

			auto get_return_object()
			{
				std::cout << __func__ << std::endl;

				return coro_ret<T>{handle_type::from_promise(*this)};
			}

			std::suspend_always initial_suspend()
			{
				std::cout << __func__ << std::endl;

				return std::suspend_always();
			}
			std::suspend_always final_suspend() noexcept
			{
				std::cout << __func__ << std::endl;

				return std::suspend_always();
			}

			void unhandled_exception()
			{
				std::cout << __func__ << std::endl;
				std::terminate();
			}

			void return_value(T t)
			{
				m_return_t = t;

				std::cout << __func__ << std::endl;
			}

			std::suspend_always yield_value(T t)
			{
				m_return_t = t;

				std::cout << __func__ << std::endl;

				return std::suspend_always();
			}

			//
			T m_return_t;
		};

		//
		handle_type m_coro_handle;
	};

	template <typename T>
	coro_ret<T> coroutine_7in7out()
	{
		// 进入协程看initial_suspend，返回std::suspend_always{};会有一次挂起
		std::cout << "Coroutine co_await std::suspend_never" << std::endl;

		// co_await std::suspend_never{} 不会挂起
		co_await std::suspend_never{};

		std::cout << "Coroutine co_await std::suspend_always" << std::endl;
		co_await std::suspend_always{};

		std::cout << "Coroutine stage 1 ,co_yield" << std::endl;
		co_yield 101;

		std::cout << "Coroutine stage 2 ,co_yield" << std::endl;
		co_yield 202;

		std::cout << "Coroutine stage 3 ,co_yield" << std::endl;
		co_yield 303;

		std::cout << "Coroutine stage end, co_return" << std::endl;
		co_return 808;
	}

	void test()
	{
		bool done = false;

		// 调用协程,得到返回值c_r，后面使用这个返回值来管理协程。
		std::cout << "Start coroutine_7in7out ()\n";
		auto c_r = coroutine_7in7out<int>();

		// 第一次停止因为initial_suspend 返回的是suspend_always
		// 此时没有进入Stage 1
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;

		// 此时是，co_await std::suspend_always{}
		done = c_r.move_next();
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;

		// 此时打印Stage 1
		done = c_r.move_next();
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;

		done = c_r.move_next();
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;

		done = c_r.move_next();
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;

		done = c_r.move_next();
		std::cout << "Coroutine " << (done ? "is done " : "isn't done ")
			<< "ret =" << c_r.get() << std::endl;
	}

} // namespace coroutine_7in7out


#endif